<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html lang="en-US">
<head>
<title>NMLSET Class</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="http://www.isd.mel.nist.gov/mel2.css" type="text/css" />

<style type="text/css">
  div.user{
    width: 0
    font-family: monospaced;
    font-size: smaller;
    background: rgb(235,235,255);
    padding: 0.1em; 
    overflow: auto;
    overflow-y: visible;
  }
  div.output{
    width: 0
    font-family: monospaced;
    font-size: smaller;
    background: rgb(235,255,235);
    padding: 0.1em;
    overflow: auto;
    overflow-y: visible;
  }
  div.file{
    width: 0
    font-family: monospaced;
    font-size: smaller;
    background: rgb(255,235,235);
    padding: 0.1em;
    border: thin solid black;
    overflow: auto;
    overflow-y: visible;
  }
  em.var {
    font-style: italic;
    font-family: fantasy;
    font-size: larger;
   }
  strong.bnfterminal {
    font-weight:bolder;
  }
</style>
</head>
<body>
<h1>NMLSET Class</h1>

<ul>
<li><a href="#introduction">Introduction</a></li>
<li><a href="#examples">Examples</a></li>
<li><a href="#apireference">API Reference</a>
<ul>
<li><a href="#nmlset_class">NMLSET</a></li>
<li><a href="#nmldomainset_class">NMLDOMAINSET</a></li>
</ul>
</li>
</ul>

<h2><a name="introduction">Introduction</a></h2>

<p>The NML Set class is a class intended to make it easier to write applications  to monitor or interact with multiple NML buffers especially when the number of such buffers is not fixed or known when the application starts. It is designed to work with the <a href="nmlcfgsvr.html">NML Configuration Server</a>, see <a href="http://www.isd.mel.nist.gov/projects/rcslib/nmlcfgsvr.html">http://www.isd.mel.nist.gov/projects/rcslib/nmlcfgsvr.html</a>. </p>

<h3><a name="NOTATION">Notation</a></h3>

<p>I like lots of examples.</p>

<div class="user">
<pre>

     Commands users are expected to enter at a command prompt will look like this.

</pre>
</div>

<div class="output">
<pre>

     Computer program generated example output will look like this.

</pre>
</div>

<div class="file">
<pre>

Text files listed in line look like this.

</pre>
</div>

<h2><a name="examples">Examples</a></h2>

<p>It is convenient for testing to have a set of applications where the buffer name, process name and configuration file can be set from the command line. I do not expect most applications to be built this way but it allows us to experiment with a number of different scenarios without recompiling anything. To run the demonstrations you need four programs nmlcfgsvr which should have been built along with the RCS library and two programs just for testing: nml_test_write, nml_test_nmlset. The source for the three test programs includes:
<a href="nml_test_nmlset.cc">nml_test_nmlset.cc</a>,
<a href="nml_test_write.cc">nml_test_write.cc</a>,
<a href="nml_test_format.hh">nml_test_format.hh</a>,
<a href="nml_test_format_n.cc">nml_test_format_n.cc</a>, and
<a href="nml_test_format_n_codegen_protos.hh">nml_test_format_n_codegen_protos.hh</a>. (nml_test_format_n.cc, and nml_test_format_n_codegen_protos.hh were autogenerated from nml_test_format.hh using the <a href="CodeGen-Instructions.html">NML Code Generator</a> see <a href="http://www.isd.mel.nist.gov/projects/rcslib/CodeGen-Instructions.html">http://www.isd.mel.nist.gov/projects/rcslib/CodeGen-Instructions.html</a>.) They should be in the src/test directory of the expanded RCS library source archive or you can download them from the last set of links.</p>

<h3>Compiling the test programs:</h3>

<p>Exactly how you compile them depends on your operating system, compiler and
how directories are laid out. This worked for me:</p>

<div class="user">
<pre>

g++ -Ircslib_install_dir/include -Ircslib-2004.6/src/test  rcslib-2004.6/src/test/nml_test_write.cc rcslib-2004.6/src/test/nml_test_format_n.cc -Lrcslib_install_dir/lib -lrcs -o nml_test_write

g++ -Ircslib_install_dir/include -Ircslib-2004.6/src/test  rcslib-2004.6/src/test/nml_test_nmlset.cc rcslib-2004.6/src/test/nml_test_format_n.cc -Lrcslib_install_dir/lib -lrcs -o nml_test_nmlset

</pre>
</div>

<p>rcslib_install_dir is unique to my system. Generally that needs to be replaced in all commands with something appropriate to your system, or you could use a symbolic link to make that work.</p>

<p>The programs should also be built if &quot;make check&quot; were run.</p>


<h3>Running some demonstrations:</h3>

<p>The examples work as follows: Two instances of the nmlcfgsvr are launched. The nml_test_nmlset application begins monitoring those servers for a set of buffers to be created. Multiple instances of nml_test_write are launched each creating a new buffer and repeatedly writing to it. The nml_test_nmlset will repeatedly print some cryptic information indicating how many buffers have been created and the current data in each one.</p>

<p>Start the nmlcfgsvr:</p>

<div class="user">
<pre>

rcslib_install_dir/bin/nmlcfgsvr &amp;

</pre>
</div>

<p>Start another nmlcfgsvr connecte to port 50000:</p>

<div class="user">
<pre>

rcslib_install_dir/bin/nmlcfgsvr --port 50000 &amp;

</pre>
</div>

<p>Start the nml_test_nmlset application:</p>

<div class="user">
<pre>

./nml_test_nmlset b1\;b2 ntns nmlcfgsvr:\;nmlcfgsvr::50000 

</pre>
</div>

<p>The three command line arguments are passed to the NMLSET constructor. The first argument is a semicolon delimited list of buffer names, the second argument is a process name, which currently has no impact, and the third argument is a semicolon seperated list of nmlcfgsvrs.  The semicolons need to be escaped with a backslash to prevent the shell from breaking the command there.</p>

<p>The nml_test_nmlset application will poll both the nmlcfgsvr's wait 2 seconds and then try again. It prints a cycle count, giving the number of times it has done this and a line of astericks to seperate the output in each cycle. At the beginning there is no other output because there are no buffers created matching the nmlset criteria. </p>

<p>Start several instances of nml_test_write to create different buffers.
These could be launched from different terminals or just backgrounded in one terminal. You would probably want to take notice of the the change in the nml_test_nmlset programs output as each program is started or killed and experiment with killing and starting them in different orders.</p>

<div class="user">
<pre>

./nml_test_write b1 ntw nmlcfgsvr: 199 -1 &amp;
./nml_test_write b2 ntw nmlcfgsvr: 198 -1 &amp;
./nml_test_write b1 ntw nmlcfgsvr::50000  197 -1 &amp;
./nml_test_write b2 ntw nmlcfgsvr::50000  196 -1 &amp;
env NMLCFGSVR_DOMAIN=d2 ./nml_test_write b1 ntw nmlcfgsvr: 195 -1 &amp;
env NMLCFGSVR_DOMAIN=d2 ./nml_test_write b2 ntw nmlcfgsvr: 194 -1 &amp;
env NMLCFGSVR_DOMAIN=d2 ./nml_test_write b1 ntw nmlcfgsvr::50000  193 -1 &amp;
env NMLCFGSVR_DOMAIN=d2 ./nml_test_write b2 ntw nmlcfgsvr::50000  192 -1 &amp;
env NMLCFGSVR_DOMAIN=~newnmldomain~ ./nml_test_write b1 ntw nmlcfgsvr: 191 -1 &amp;
env NMLCFGSVR_DOMAIN=~newnmldomain~ ./nml_test_write b2 ntw nmlcfgsvr: 190 -1 &amp;
env NMLCFGSVR_DOMAIN=~newnmldomain~ ./nml_test_write b1 ntw nmlcfgsvr::50000  189 -1 &amp;
env NMLCFGSVR_DOMAIN=~newnmldomain~ ./nml_test_write b2 ntw nmlcfgsvr::50000  188 -1 &amp;
env NMLCFGSVR_DOMAIN=~newnmldomain~ ./nml_test_write b1 ntw nmlcfgsvr: 187 -1 &amp;
env NMLCFGSVR_DOMAIN=~newnmldomain~ ./nml_test_write b2 ntw nmlcfgsvr: 186 -1 &amp;
env NMLCFGSVR_DOMAIN=~newnmldomain~ ./nml_test_write b1 ntw nmlcfgsvr::50000  185 -1 &amp;
env NMLCFGSVR_DOMAIN=~newnmldomain~ ./nml_test_write b2 ntw nmlcfgsvr::50000  184 -1 &amp;

</pre>
</div>

<p>The first argument to nml_test_write is the buffer name, the second is the process name, the third is the NML configuration file name or nmlcfgsvr pseudo file name, the fourth is an integer that the lastvar variable of the test message will be set to make it easy to identify which instance of nml_test_write wrote which message being displayed by the nml_test_nmlset application, and the fifth argument is the repeat count with -1 indicating that nml_test_write should repeat forever. The env command is used to run another command in a modified environment. The environment variable NMLCFGSVR_DOMAIN allows multiple independant buffers with the same name to be created on the same nmlcfgsvr. The value of &quot;~newnmldomain~&quot; has special significance in that it means a new unique domain name is used for each process. Once all of these processes are running there should be sixteen independant NML buffers created. Eight will be named b1 and eight will be named b2. Four of the eight that are named b1 will be registered with the nmlcfgsvr that is bound to default port and four will be registered to the nmlcfgsvr bound to port 5000. Within each set of four the buffers are distinguished only by their domain which was set using the environment variable NMLCFGSVR_DOMAIN.</p>

<p>Looking at the status of one of the nmlcfgsvr's might make things clearer.</p>

<div class="user">
<pre>

telnet localhost 50000

</pre>
</div>
<div class="output">
<pre>

Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.

</pre>
</div>
<div class="user">
<pre>

list

</pre>
</div>
<div class="output">
<pre>

#BEGIN_LIST
B       b1      SHMEM   192.168.1.68     9728    0       0       2       *      1504838656      TCP=37841 bsem=1504838657  packed confirm_write nmlcfgsvr=192.168.1.68:50000
B       b2      SHMEM   192.168.1.68     9728    0       0       3       *      1504838658      TCP=37842 bsem=1504838659  packed confirm_write nmlcfgsvr=192.168.1.68:50000
B       b1      SHMEM   192.168.1.68     9728    0       0       4       *      1504838660      TCP=37845 bsem=1504838661  packed confirm_write nmlcfgsvr=192.168.1.68:50000 domain=d2
B       b2      SHMEM   192.168.1.68     9728    0       0       5       *      1504838662      TCP=37846 bsem=1504838663  packed confirm_write nmlcfgsvr=192.168.1.68:50000 domain=d2
B       b1      SHMEM   192.168.1.68     9728    0       0       6       *      1504838664      TCP=37865 bsem=1504838665  packed confirm_write nmlcfgsvr=192.168.1.68:50000 domain=nd.1.ryf.jyo81.tn5.VClyf1.frTIb1.jKg2.
B       b2      SHMEM   192.168.1.68     9728    0       0       7       *      1504838666      TCP=37866 bsem=1504838667  packed confirm_write nmlcfgsvr=192.168.1.68:50000 domain=nd.2.zCf.jyo81.tn5.VClyf1.frTIb1.ZzC2.
B       b1      SHMEM   192.168.1.68     9728    0       0       8       *      1504838668      TCP=37867 bsem=1504838669  packed confirm_write nmlcfgsvr=192.168.1.68:50000 domain=nd.3.HGf.jyo81.tn5.VClyf1.frTIb1.VnM2.
B       b2      SHMEM   192.168.1.68     9728    0       0       9       *      1504838670      TCP=37869 bsem=1504838671  packed confirm_write nmlcfgsvr=192.168.1.68:50000 domain=nd.4.XOf.jyo81.tn5.VClyf1.frTIb1.9ou3.
#END_LIST

</pre>
</div>
<div class="user">
<pre>

         (press [CTRL]-D )

</pre>
</div>
<div class="output">
<pre>

Connection closed by foreign host.

</pre>
</div>

<p>This is the output from only one of the nmlcfgsvr's  which is why there are 
only eight buffers rather than sixteen. Each of the four b1 buffers has a different TCP port, Shared memory key, buffer number and 
domain. ( Unfortunately the domain is at the end of the line and depending on browser font settings and screen resolution/paper size 
have likely scrolled of the right side of the screen/page.)
</p>

<p>For reference here is the source code for the nml_test_nmlset application:</p>

<div class="file">
<pre>

#include &quot;rcs.hh&quot;		// Required for all RCS library applications.
#include &quot;nmlset.hh&quot;		// Declare the NMLSET,NMLDOMAINSET and NMLDOMAINSET_MEMBER classes.

#include &quot;nml_test_format.hh&quot;	// Specific message classes for these examples.

// Standard C/C++ headers
#include &lt;stdlib.h&gt;
#include &lt;signal.h&gt;
#include &lt;stdio.h&gt;


// Setup Control-C signal handler for quitting this application.
static void (*old_sigint_handler)(int) =0;
static bool sigint_occured=false;

static void sigint_handler(int sig)
{
  if(old_sigint_handler &amp;&amp; old_sigint_handler != SIG_IGN &amp;&amp;
     old_sigint_handler != SIG_DFL)
    {
      (*old_sigint_handler)(sig);
    }
  sigint_occured=true;
}

 
int
main(int argc, const char **argv)
{
  // Parse/handle  the command line arguments.
  if(argc &lt; 4)
    {
      fprintf(stderr,&quot;nml_test_nmlset usage: bufferlist procname cfgsvrlist [buffersneeded] [maxcycles]\n&quot;);
      exit(1);
    }
  int bufsneeded = -1;
  if(argc &gt;= 5)
    {
      bufsneeded = strtol(argv[4],0,0);
      printf(&quot;bufsneeded=%d\n&quot;,bufsneeded);
    }
  int maxcycles = -1;
  if(argc &gt;= 6)
    {
      maxcycles = strtol(argv[5],0,0);
      printf(&quot;maxcycles=%d\n&quot;,maxcycles);
    }
  int bufsread = 0;
  int cycles = 0;
  bool error_occured=false;
 

  // Create an NMLSET class object that will be used to
  // monitor all buffers in the ; delimited list in argv[1] on
  // nmlcfgsvr's listed in the ; delimeted list in argv[3],
  // pass argv[2] as our ProcessName.
  NMLSET s(nml_test_format,argv[1],argv[2],argv[3]);
  printf(&quot;NMLSET s(nml_test_format,argv[1]=%s,argv[2]=%s,argv[3]=%s);\n&quot;,
	 argv[1],argv[2],argv[3]);

  int *lastnum = 0;
  if(bufsneeded &gt; 0)
    {
      lastnum = new int[bufsneeded];
    }
  for(int k = 0; k &lt; bufsneeded; k++)
    {
      lastnum[k] = 0;
    }

  // Setup the signal handler so we can gracefully quit when
  // someone presses Control-C.
  old_sigint_handler = signal(SIGINT,sigint_handler);
  
  while(!sigint_occured)
    {
      cycles++;
      if(maxcycles &gt; 0 &amp;&amp; cycles &gt; maxcycles)
	{
	  break;
	}
      printf(&quot;cycles=%d\n&quot;,cycles);
      printf(&quot;\n**********************************************************\n&quot;);

      // Check all of the configuration servers to see if there are new
      // buffers that match our criteria. If a server won't respond after
      // 1.0 seconds go on to the next one or return if it is the last one.
      s.update_set(1.0);
      printf(&quot;s..update_set(1.0);\n&quot;);

      // NMLSETS are divided into NMLDOMAINSETS, there could be an
      // infinite number, so we run though the list, by calling s.get_first_domainset() and then s.get_next_domainset() until it returns NULL.
      NMLDOMAINSET *nd = s.get_first_domainset();
      printf(&quot;NMLDOMAINSET *nd = s.get_first_domainset();\nnd=%p\n&quot;,
	     (void*)nd);
      while(nd)
	{
	  printf(&quot;nd-&gt;getdomainname() = %s\n&quot;,nd-&gt;getdomainname());
	  printf(&quot;nd-&gt;cfgsvrname() = %s\n&quot;,nd-&gt;getcfgsvrname());
	  
	  // Each NMLDOMAINSET has a fixed randomly accessible list of 
	  // members. The member id's correspond to the position of the
	  // buffer name in the bufferlist passed when the top level NMLSET
	  // object was created.
	  for(int i=0; i &lt;= nd-&gt;get_max_member_id(); i++)
	    {
	      NMLDOMAINSET_MEMBER *nm = nd-&gt;get_member(i);
	      printf(&quot;NMLDOMAINSET_MEMBER *nm = nd-&gt;get_member(i=%d);\nnm=%p\n&quot;,i,nm);

	      if(nm)
		{
		  if(nm-&gt;cms &amp;&amp; nm-&gt;cms-&gt;BufferName)
		    {
		      printf(&quot;nm-&gt;cms-&gt;BufferName=%s\n&quot;,nm-&gt;cms-&gt;BufferName);
		    }
		  NMLTYPE t;

		  // NMLDOMAINSET_MEMBER is publically derived from NML
		  // so we can do anything with nm that we could do 
		  // with an NML *, including read, write, peek, get_address()
		  // etc.
		  t = nm-&gt;peek();
		  if(t &lt; 0)
		    {
		      error_occured=true;
		    }
		  printf(&quot;nm-&gt;peek() returned %d\n&quot;,t);
		  if(t == TEST_MESSAGE_TYPE)
		    {
		      TEST_MESSAGE *tm = (TEST_MESSAGE *) nm-&gt;get_address();
		      printf(&quot;TEST_MESSAGE *tm = (TEST_MESSAGE *) nm-&gt;get_address();\ntm=%p\n&quot;,(void *)tm);
		      printf(&quot;tm-&gt;i = %d, tm-&gt;lastvar=%d\n&quot;,tm-&gt;i,tm-&gt;lastvar);
		      int j=0;
		      if(tm-&gt;lastvar &gt; 0 &amp;&amp; bufsneeded &gt; 0)
			{
			  for(j =0; j &lt; bufsread; j++)
			    {
			      printf(&quot;j=%d,lastnum[j]=%d,bufsread=%d\n&quot;,
				     j,lastnum[j],bufsread);
			      if(lastnum[j] == tm-&gt;lastvar)
				{
				  break;
				}
			    }
			  printf(&quot;j=%d,lastnum[j]=%d,bufsread=%d\n&quot;,
				 j,lastnum[j],bufsread);
			  if(lastnum[j] == 0 &amp;&amp; j == bufsread)
			    {
			      printf(&quot;new buffer found. -- bufsread=%d, j=%d,lastnum[j]=%d,tm-&gt;lastvar=%d\n&quot;,
				     bufsread,j,lastnum[j],tm-&gt;lastvar);
			      lastnum[j] = tm-&gt;lastvar;
			      bufsread++;
			      if(bufsread &gt;= bufsneeded)
				{
				  break;
				}
			    }
			}
		    }
		  else
		    {
		      // Deleting an NMLDOMAINSET_MEMBER removes it from
		      // its parent NMLDOMAINSET.
		      printf(&quot;delete nm;\n&quot;);
		      delete nm;
		    }
		}
	    }
	  if(bufsneeded &gt; 0 &amp;&amp; bufsread &gt;= bufsneeded)
	    {
	      break;
	    }
	  nd = s.get_next_domainset();
	  printf(&quot;NMLDOMAINSET *nd = s.get_next_domainset();\nnd=%p\n&quot;,
		 (void*)nd);
	}
      if(bufsneeded &gt; 0 &amp;&amp; bufsread &gt;= bufsneeded)
	{
	  break;
	}
      printf(&quot;esleep(2.5);\n&quot;);
      fflush(stdout);
      esleep(2.5);
    }

  // clean up the signal handler.
  signal(SIGINT,old_sigint_handler);


  // Set the return value so we can use this program in test scripts.
  if(error_occured)
    {
      exit(2);
    }
  if(cycles &gt; maxcycles &amp;&amp; maxcycles &gt; 0)
    {
      exit(3);
    }
  exit(0);
}

</pre>
</div>

<p>The program is somewhat more verbose than an example should be due to its dual use as an implementation testing program.
The key points to notice are that:</p>
<ul>
<li>An NMLSET object named s is created when the program is first started using the command line arguments and a pointer to the nml_test_format function which is declared in nml_test_format.hh and allows this particular NMLSET object and the NMLDOMAINSET and NMLDOMAINSET_MEMBER objects that will later be obtained from it  to decode and encode messages specific to this example.</li>
<li>Each cycle s.update_set() is called. Only during this call can the possible NMLDOMAINSET and NMLDOMAINSET_MEMBER objects than can be obtained from this set change.</li>
<li>After calling s.update_set(), a list of NMLDOMAINSET objects is obtained by first calling s.get_first_domainset() and then calling s.get_next_domainset() until an NULL pointer is returned.</li>
<li>Each NMLDOMAINSET stores a list of NML connection objects within a single domain.</li>
<li>Each of these NML connection objects is obtained by calling nd->get_member(). The returned pointer is of type NMLDOMAINSET_MEMBER *.</li>
<li>Since NMLDOMAINSET_MEMBER is publically derived from NML it can be used just any other NML object could be used as described in 
<a href="http://www.isd.mel.nist.gov/projects/rcslib/NMLcpp.html">The NML Programmer's Guide (C++ Version)</a>. Except that programmer's should be aware that it should not be dereferenced after the NMLDOMAINSET it was obtained from is deleted, or the NMLSET from which that was obtained is deleted.
</li>
</ul>

<p>The following is a portion of the output from the nml_test_nmlset application:</p>

<div class="output" >
<pre>

cycles=33

**********************************************************
s..update_set(1.0);
NMLDOMAINSET *nd = s.get_first_domainset();
nd=0x9fbc208
nd->getdomainname() = 
nd->cfgsvrname() = 
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=0x9fb77d0
nm->cms->BufferName=b1
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fb9948
tm->i = 53, tm->lastvar=199
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=0x9fbc2a8
nm->cms->BufferName=b2
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fbe420
tm->i = 52, tm->lastvar=198
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0x9fc5668
nd->getdomainname() = d2
nd->cfgsvrname() = 
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=0x9fc0c98
nm->cms->BufferName=b1
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fc2e10
tm->i = 47, tm->lastvar=195
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=0x9fd3620
nm->cms->BufferName=b2
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fd5780
tm->i = 44, tm->lastvar=194
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0x9fca0d0
nd->getdomainname() = 
nd->cfgsvrname() = :50000
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=0x9fc5700
nm->cms->BufferName=b1
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fc7878
tm->i = 46, tm->lastvar=197
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=0x9fca170
nm->cms->BufferName=b2
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fcc2e8
tm->i = 47, tm->lastvar=196
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0x9fd3530
nd->getdomainname() = nd.1.kH1.jyo81.d4a.VClyf1.WHgIb1.Xju1.
nd->cfgsvrname() = 
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=0x9fceb60
nm->cms->BufferName=b1
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fd0cd8
tm->i = 46, tm->lastvar=187
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=(nil)
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0x9fdc9c0
nd->getdomainname() = d2
nd->cfgsvrname() = :50000
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=0x9fd7ff0
nm->cms->BufferName=b1
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fda168
tm->i = 46, tm->lastvar=193
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=0x9fdca58
nm->cms->BufferName=b2
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fdebd0
tm->i = 46, tm->lastvar=192
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0x9fe5e10
nd->getdomainname() = nd.3.Kq2.jyo81.d4a.VClyf1.ZHgIb1.6ZE.
nd->cfgsvrname() = 
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=0x9fe1440
nm->cms->BufferName=b1
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fe35b8
tm->i = 44, tm->lastvar=191
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=(nil)
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0x9fea8c0
nd->getdomainname() = nd.2.Cm2.jyo81.d4a.VClyf1.ZHgIb1.Fdk.
nd->cfgsvrname() = 
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=(nil)
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=0x9fe5f28
nm->cms->BufferName=b2
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fe8088
tm->i = 44, tm->lastvar=190
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0x9fef368
nd->getdomainname() = nd.1.me2.jyo81.tn5.VClyf1.YHgIb1.KNJ3.
nd->cfgsvrname() = :50000
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=0x9fea9b0
nm->cms->BufferName=b1
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fecb10
tm->i = 44, tm->lastvar=185
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=(nil)
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0x9ff3e10
nd->getdomainname() = nd.2.ui2.jyo81.tn5.VClyf1.ZHgIb1.Fo6.
nd->cfgsvrname() = :50000
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=(nil)
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=0x9fef458
nm->cms->BufferName=b2
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9ff15b8
tm->i = 44, tm->lastvar=184
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0x9ff88b8
nd->getdomainname() = nd.4.8D2.jyo81.d4a.VClyf1.ZHgIb1.qL91.
nd->cfgsvrname() = 
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=(nil)
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=0x9ff3f00
nm->cms->BufferName=b2
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9ff6060
tm->i = 44, tm->lastvar=186
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0x9ffd368
nd->getdomainname() = nd.4.0z2.jyo81.tn5.VClyf1.ZHgIb1.wHX.
nd->cfgsvrname() = :50000
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=0x9ff89d0
nm->cms->BufferName=b1
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9ffab30
tm->i = 44, tm->lastvar=189
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=(nil)
NMLDOMAINSET *nd = s.get_next_domainset();
nd=0xa001e18
nd->getdomainname() = nd.3.Su2.jyo81.tn5.VClyf1.ZHgIb1.PbN.
nd->cfgsvrname() = :50000
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=0);
nm=(nil)
NMLDOMAINSET_MEMBER *nm = nd->get_member(i=1);
nm=0x9ffd480
nm->cms->BufferName=b2
nm->peek() returned 101
TEST_MESSAGE *tm = (TEST_MESSAGE *) nm->get_address();
tm=0x9fff5e0
tm->i = 44, tm->lastvar=188
NMLDOMAINSET *nd = s.get_next_domainset();
nd=(nil)
esleep(2.5);
cycles=34

**********************************************************

</pre>
</div>

<p>Notes concerning the above output:</p>
<ul>
<li>There were twelve NMLDOMAINSET objects returned in the single cycle 
overwhich output was obtained.</li>
<li>Four of them had both a b1 and a b2, four had only a b1 and four had only a b2.</li>
<li>Seperate NMLDOMAINSET objets were returned with the same domainname when
they corresponded to different cfgsvrs.</li>
<li>The connection to a b2 buffer was always retrieved with nd->get_member(1) even when
there was no b1 buffer and nd->get_member(0) had therefore returned NULL.</li>
<li>The nmlcfgsvr names do not include the common &quot;nmlcfgsvr:&quot; prefix.</li>
</ul>

<h2><a name="apireference">API Reference</a></h2>

<h3><a name="nmlset_class">NMLSET</a></h3>

<p>NMLSET is the main class from which objects of type NMLDOMAINSET and NMLDOMAINSET_MEMBER are obtained.</p>

<p>From &quot;nmlset.hh&quot;:</p>

<div class="file">
<pre>

class NMLSET
{
public:
  NMLSET(NML_FORMAT_PTR f_ptr, 
	 const char *buflist, 
	 const char *procname, 
	 const char *cfglist);
  ~NMLSET();

  class NMLDOMAINSET  *get_first_domainset();
  class NMLDOMAINSET  *get_next_domainset();
  void update_set(double _timeout);

// Other private functions and data members skipped.
// . . .

};

</pre>
</div>

<p>The constructor takes four arguments. The format function is used with all of the NML channels created through this NMLSET. If the 
channels need different format functions, pass NULL for this argument and use NML::prefix_format_function() to set the format function on each NMLDOMAINSET_MEMBER obtained later. The buflist is a semicolon delimited list of buffer names. The procname is the name of the calling process it is passed to the each nmlcfgsvr and could be used if the nmlcfgsvr needed to be configured to handle this process differently or more likely just to improve diagnostics output, but is typically ignored. The cfglist is a semicolon delimited list of nmlcfgsvrs see 
<a href="nmlcfgsvr.html#pseudo_filename">NML Configuration Server:Pseudo File Name Syntax</a>. update_set() queries the nmlcfgsvrs for new domains or additional buffers from the list in the existing domains. The timout is passed to each attempt to query so since multiple queries occur over the update_set the time update_set takes to return could be longer than the timeout. A timeout of -1.0 can be used if the queries should never timeout. The functions get_first_domainset and get_next_domainset should be used to iterrate through the list of NMLDOMAINSETs. If the NMLSET object is deleted, pointers to NMLDOMAINSET and NMLDOMAINSET_MEMBERS optained through it should no longer be accessed as they will be deleted as well.</p>

<h3><a name="nmldomainset_class">NMLDOMAINSET</a></h3>

<p>NMLDOMAINSET class is used to store the is of NML connections obtained within a single domain.</p>

<p>From &quot;nmlset.hh&quot;:</p>

<div class="file">
<pre>

class NMLDOMAINSET
{
public:
  ~NMLDOMAINSET();

  class NMLDOMAINSET_MEMBER  *get_member(int id);
  const char *getdomainname() const;
  const char *getcfgsvrname() const;
  int get_max_member_id() const;
 
// Private members and functions skipped.
// . . .
};

</pre>
</div>

<p>get_member() returns the NMLDOMAINSET_MEMBER associated with a given NML connection. It should not be accessed after the NMLDOMAINSET object is deleted. The id should be between 0 and get_max_member_id. NULL will be returned by get_member if no valid NML connection could be obtained for this buffer in the current domain. get_max_member_id always returns an integer one less than the number of buffers in the list passed to the NMLSET constructor. getdomainname returns the domain name associated with this object. The getcfgsvrname() returns the portion of the nmlcfgsvr's pseudo file name after the &quot;nmlcfgsvr:&quot; prefix.</p>

<hr />

<p>Last Modified: July 12,2004</p>

<p>If you have questions or comments regarding this page please
contact  <a href="http://www.isd.mel.nist.gov/personnel/shackleford/index.htm"
>Will Shackleford</a> at <i><a href="mailto:shackle@cme.nist.gov">shackle@cme.nist.gov</a></i></p>
</body>
</html>




